namespace createjs {

	export class Container extends DisplayObject {
		children: DisplayObject[];
		tickChildren: boolean;

		constructor() {
			super();
			this.children = [];
			this.tickChildren = true;
		}

		getNumChildren() {
			return this.children.length;
		};

		get numChildren() {
			return this.getNumChildren();
		}


		isVisible() {
			var hasContent = this.children.length;
			return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
		};

		addChild(child: DisplayObject) {
			if (child == null) { return child; }
			var l = arguments.length;
			if (l > 1) {
				for (var i = 0; i < l; i++) { this.addChild(arguments[i]); }
				return arguments[l - 1];
			}
			// Note: a lot of duplication with addChildAt, but push is WAY faster than splice.
			var par = child.parent, silent = par === this;
			par && par._removeChildAt(createjs.indexOf(par.children, child), silent);
			child.parent = this;
			this.children.push(child);
			if (!silent) {
				child.dispatchEvent("added");
			}
			return child;
		};

		addChildAt(child: DisplayObject, index: number) {
			var l = arguments.length;
			var indx = arguments[l - 1]; // can't use the same name as the index param or it replaces arguments[1]
			if (indx < 0 || indx > this.children.length) { return arguments[l - 2]; }
			if (l > 2) {
				for (var i = 0; i < l - 1; i++) { this.addChildAt(arguments[i], indx + i); }
				return arguments[l - 2];
			}
			var par = child.parent, silent = par === this;
			par && par._removeChildAt(createjs.indexOf(par.children, child), silent);
			child.parent = this;
			this.children.splice(index, 0, child);
			if (!silent) { child.dispatchEvent("added"); }
			return child;
		};


		removeChild(child: DisplayObject) {
			var l = arguments.length;
			if (l > 1) {
				var good = true;
				for (var i = 0; i < l; i++) { good = good && this.removeChild(arguments[i]); }
				return good;
			}
			return this._removeChildAt(createjs.indexOf(this.children, child));
		};


		removeChildAt(index: number) {
			var l = arguments.length;
			if (l > 1) {
				var a = [];
				for (var i = 0; i < l; i++) { a[i] = arguments[i]; }
				a.sort(function (a, b) { return b - a; });
				var good = true;
				for (var i = 0; i < l; i++) { good = good && this._removeChildAt(a[i]); }
				return good;
			}
			return this._removeChildAt(index);
		};

		removeAllChildren() {
			var kids = this.children;
			while (kids.length) { this._removeChildAt(0); }
		};


		getChildAt(index) {
			return this.children[index];
		};

		getChildByName(name) {
			var kids = this.children;
			for (var i = 0, l = kids.length; i < l; i++) {
				if (kids[i].name == name) { return kids[i]; }
			}
			return null;
		};

		sortChildren(sortFunction) {
			this.children.sort(sortFunction);
		};

		getChildIndex(child: DisplayObject) {
			return createjs.indexOf(this.children, child);
		};

		swapChildrenAt(index1: number, index2: number) {
			var kids = this.children;
			var o1 = kids[index1];
			var o2 = kids[index2];
			if (!o1 || !o2) { return; }
			kids[index1] = o2;
			kids[index2] = o1;
		};


		swapChildren(child1: DisplayObject, child2: DisplayObject) {
			var kids = this.children;
			var index1, index2;
			for (var i = 0, l = kids.length; i < l; i++) {
				if (kids[i] == child1) { index1 = i; }
				if (kids[i] == child2) { index2 = i; }
				if (index1 != null && index2 != null) { break; }
			}
			if (i == l) { return; } // TODO: throw error?
			kids[index1] = child2;
			kids[index2] = child1;
		};


		setChildIndex(child: DisplayObject, index: number) {
			var kids = this.children, l = kids.length;
			if (child.parent != this || index < 0 || index >= l) { return; }
			for (var i = 0; i < l; i++) {
				if (kids[i] == child) { break; }
			}
			if (i == l || i == index) { return; }
			kids.splice(i, 1);
			kids.splice(index, 0, child);
		};

		contains(child: DisplayObject) {
			while (child) {
				if (child == this) { return true; }
				child = child.parent;
			}
			return false;
		};

		getBounds() {
			return this._getBounds(null, true);
		};

		getTransformedBounds() {
			return this._getBounds();
		};

		clone(recursive?: boolean) {
			var o = this._cloneProps(new Container());
			if (recursive) {
				this._cloneChildren(o);
			}
			return o;
		};


		toString() {
			return "[Container (name=" + this.name + ")]";
		};


		_tick(evtObj: Event) {
			if (this.tickChildren) {
				for (var i = this.children.length - 1; i >= 0; i--) {
					var child = this.children[i];
					if (child.tickEnabled && child._tick) {
						child._tick(evtObj);
					}
				}
			}
			super._tick(evtObj);
		};

		_cloneChildren(o) {
			if (o.children.length) { o.removeAllChildren(); }
			var arr = o.children;
			for (var i = 0, l = this.children.length; i < l; i++) {
				var clone = this.children[i].clone(true);
				clone.parent = o;
				arr.push(clone);
			}
		};


		_removeChildAt(index, silent?) {
			if (index < 0 || index > this.children.length - 1) { return false; }
			var child = this.children[index];
			if (child) { child.parent = null; }
			this.children.splice(index, 1);
			if (!silent) { child.dispatchEvent("removed"); }
			return true;
		};

		_getBounds(matrix?, ignoreTransform?) {
			var bounds = super.getBounds();
			if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }

			var mtx = this._props.matrix;
			mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
			if (matrix) { mtx.prependMatrix(matrix); }

			var l = this.children.length, rect = null;
			for (var i = 0; i < l; i++) {
				var child = this.children[i];
				if (!child.visible || !(bounds = child._getBounds(mtx))) { continue; }
				if (rect) { rect.extend(bounds.x, bounds.y, bounds.width, bounds.height); }
				else { rect = bounds.clone(); }
			}
			return rect;
		};

	}

}